/******************************************************************************/
/*  func_handlers.c                                                           */
/******************************************************************************/

#include "data_types.h"

#include "port\port.h"
#include "mb.h"
#include "mbframe.h"
#include "mbproto.h"
#include "functions\mbutils.h"

#include "Relay\relay.h"
#include "Din\din.h"
#include "Device\device.h"
#include "Fmem_layer\fmem_layer.h"
#include "Sett_mem\sett_mem.h"
#include "Mb_master\mb_master.h"
#include "Rfile\rfile.h"

#include "TNKernel\tn.h"

#include "func_handlers\func_handlers.h"


//==============================================================================
// MbRegInv()
//==============================================================================
static void MbRegInv(uint8_t * const _pBuff, const uint32_t _dataLen) {

    uint8_t swop;

    for (uint32_t i = 0; i < _dataLen; i++) {
        swop = _pBuff[i + 1];
        _pBuff[i + 1] = _pBuff[i];
        _pBuff[i] = swop;
        i++;
    };
}
//==============================================================================
//==============================================================================


//==============================================================================
// ModbusRegInputCb() - ain + temp + etc
//==============================================================================
eMB_ERROR_CODE ModbusRegInputCb(uint8_t * _pRegBuffer,
                                const uint16_t _address,
                                const uint16_t _numRegs) {

    eMB_ERROR_CODE err = MB_ENOREG;
    __RFILE_DATA data;

    // Read data from RFILE
    data.addr = (_address) ? ((_address - 1) * 2) : (0xFFFF * 2);
    data.dataLen = _numRegs * 2;
    data.pBuff = _pRegBuffer;

    if (RFILE::ReadRfile(&data) == TRUE_T) {
        err = MB_ENOERR;
        MbRegInv(data.pBuff, data.dataLen);
    };

    return err;
}
//==============================================================================
//==============================================================================


//==============================================================================
// ModbusRegHoldingCb() - memory
//==============================================================================
eMB_ERROR_CODE ModbusRegHoldingCb(uint8_t * _pRegBuffer,
                                  const uint16_t _address,
                                  const uint16_t _numRegs,
                                  const eMB_REGISTER_MODE _mode) {

    __FMEM_DATA mem;
    sint32_t err;

    mem.addr = (_address) ? ((_address - 1) * 2) : (0xFFFF * 2);
    mem.pBuff = _pRegBuffer;

    
    if (mem.addr > (GSETT_MEM_SIZE - 2)) {
        return MB_ENOREG;
    };
    
    if ((mem.addr + _numRegs * 2) > GSETT_MEM_SIZE) {
        return MB_EINVAL;
    };

    if (mem.dataLen) {
        return MB_ETIMEDOUT;
    };

    mem.dataLen = _numRegs * 2;

    // Write/Read data
    if (_mode == MB_REG_WRITE) {
        uint8_t * pBuff;

        // Get memory block
        err = tn_fmem_get_polling((TN_FMP *)DEVICE::Get_mem_pool(mem.dataLen + sizeof(__FMEM_DATA)),
                                  (void **)&(pBuff));

        if (err != TERR_NO_ERR) {
            return MB_ETIMEDOUT;
        };

        // Copy data in allocate buff
        ((__FMEM_DATA *)pBuff)->addr = mem.addr;
        ((__FMEM_DATA *)pBuff)->dataLen = mem.dataLen;
        ((__FMEM_DATA *)pBuff)->pBuff = pBuff + sizeof(__FMEM_DATA);

        memcpy(((__FMEM_DATA *)pBuff)->pBuff, _pRegBuffer, mem.dataLen);

        // Invert data
        MbRegInv(((__FMEM_DATA *)pBuff)->pBuff, mem.dataLen);

        // Send signal
        err = tn_queue_send_polling((TN_DQUE *)DEVICE::Get_device_sett_dque(), pBuff);

        if (err == TERR_NO_ERR) {
            return MB_ACKNOWLEDGE;
        };

        // Realese memory
        tn_fmem_release((TN_FMP *)DEVICE::Get_mem_pool(mem.dataLen + sizeof(__FMEM_DATA)), pBuff);
        return (err == TERR_TIMEOUT) ? MB_ETIMEDOUT : MB_ENORES;
    }
    else {
        // Read data from flash
        err = DEVICE::ReadSett(&mem);

        switch (err)
        {
            //------------------------------------------------------------------
            case eMEM_OK:
              // Invert data
              MbRegInv(_pRegBuffer, _numRegs * 2);
              return MB_ENOERR;

            //------------------------------------------------------------------
            case eMEM_BUSY:
              return MB_ETIMEDOUT;

            //------------------------------------------------------------------
            case eMEM_NOT_AVAILABLE:
              return MB_ENORES;

            //------------------------------------------------------------------
            case eMEM_ADDR_ERROR:
              return MB_ENOREG;

            //------------------------------------------------------------------
            case eMEM_DATA_ERROR:
              return MB_EINVAL;

            //------------------------------------------------------------------
            case eMEM_WRITE_EN_ERROR:
            //------------------------------------------------------------------
            case eMEM_MUTEX_ERROR:
            //------------------------------------------------------------------
            case eMEM_INTERNAL_ERROR:
            //------------------------------------------------------------------
            default:
              return MB_EIO;
        };
    };
}
//==============================================================================
//==============================================================================


//==============================================================================
// ModbusRegCoilsCb() - relay
//==============================================================================
eMB_ERROR_CODE ModbusRegCoilsCb(uint8_t * _pRegBuffer,
                                const uint16_t _address,
                                const uint16_t _numCoils,
                                const eMB_REGISTER_MODE _mode) {

    uint32_t addr = (_address - 1);
    uint32_t numCoils = _numCoils;

    // Test addr range, data lenght and busy
    if (addr > 1) {
        return MB_ENOREG;
    };

    if (((addr + numCoils) > 2) || (numCoils == 0)) {
        return MB_ENOREG;
    };

    // Process
    uint32_t i = 0;

    if (_mode == MB_REG_WRITE) {
        while (numCoils--) {
            const bool_t isCoilOn = (_pRegBuffer[0] & (1 << i++)) ? TRUE_T : FALSE_T;

            if (isCoilOn == TRUE_T) {
                RELAY::Relay_On((addr == 0) ? eREL_1 : eREL_2);
            }
            else {
                RELAY::Relay_Off((addr == 0) ? eREL_1 : eREL_2);
            };
            addr++;
        };
    }
    else {
        _pRegBuffer[0] = 0;

        while (numCoils--) {
            _pRegBuffer[0] |= (RELAY::GetStateRelay((addr == 0) ? eREL_1 : eREL_2))
                                               ? (1 << i) : 0;
            addr++;
            i++;
        };
    };

    return MB_ENOERR;
}
//==============================================================================
//==============================================================================


//==============================================================================
// ModbusRegDiscreteCb() - din
//==============================================================================
eMB_ERROR_CODE ModbusRegDiscreteCb(uint8_t * _pRegBuffer,
                                   const uint16_t _address,
                                   const uint16_t _numDiscrete) {
    uint32_t addr = (_address - 1);
    uint32_t numDiscrete = _numDiscrete;

    // Test addr range, data lenght and busy
    if (addr > 5) {
        return MB_ENOREG;
    };

    if (((addr + numDiscrete) > 6) || (numDiscrete == 0)) {
        return MB_ENOREG;
    };

    // Process
    _pRegBuffer[0] = 0;
    uint32_t i = 0;

    while (numDiscrete--) {
        _pRegBuffer[0] |= (DIN::GetRowDin(addr++)) ? (1 << i) : 0;
        i++;
    };

    return MB_ENOERR;
}
//==============================================================================
//==============================================================================